(* help functions to handle variable states*)
(*at present just a list of pairs of variable names (String) and values (expressions, only True or False) as: [ (string, expression) ]. *)

(* Declare a variable*)

open List;;
open Ast;;
open Datatypes;;

exception Var_already_exists of string;;
exception Var_not_exists of string;;
exception Parallel_missmatch of string;;

(* Returns true if variable exist in state, false otherwise *)
let containsVar state var = mem_assoc var state;;

(* Given a state, declare and add the given variable *)
let declareVar state var = 
  if (containsVar state var) then 
    raise (Var_already_exists ("Can't declare a variable that is already declared. Var: " ^ var))
  else
    (var, True) :: state
  ;;

(* creates a new state with the given variables declared, i.e. set to True *)
let newState varList = fold_left declareVar [] varList;;

(* A predicate to test if pair is mapping for var *)
let isMapForOne var1 (var2, _) = var1 = var2;;

(* A predicate to test if pair is mapping for any var in list *)
let isMapForMany varList (var, _) = mem var varList;;

(* Restriction of state *)
let restrict state varList = filter (isMapForMany varList) state;;

let changeValue var2 value2 (var1, value1) = 	
  if (var1 = var2) then
    (var1, value2)
  else
    (var1, value1)
  ;;

(* Returns the value of var in the state if declared, and raise Var_not_exists exception otherwise *)
let getValue state var = 
	if (containsVar state var) then
		assoc var state
	else
		raise (Var_not_exists "Can't use a variable before it has been declared")
	;;
	
(* Set variable in state *)
let setVar state var value = 	
						if (containsVar state var) then
							map (changeValue var value) state
						else
							raise (Var_not_exists ("Can't set a variable that isn't declared. Var: " ^ var))
						;;
						
(* as setVar but from lists, varList and valueList must have the same length *)
let rec setVars state varList valueList =
  if (List.length varList = List.length valueList) then begin
    match varList with
      [] -> state
      |_ -> let state1 = setVar state (List.hd varList) (List.hd valueList) in
              setVars state1 (List.tl varList) (List.tl valueList)
  end else 
    raise (Parallel_missmatch ("The lists in a parallel assignment must be equal in length."))
	;;
											
let stateEmpty = function
	[] -> true
	| _ -> false
	;;
  
let trueFilter statePair = 
	let (_, v) = statePair in
		if	(v = True) then
			true
		else
			false
		;;

let addTrueAndFalse var state = 
	let state1 = declareVar state var in
	let state2 = setVar state1 var False in
	[state1; state2]
	;;

(* given a state and a list of variables, this function return a list of states where each state contains 
  the same valuations as the initial states together with an arbitrary valuation of the variables in the list. 
  All valuations of the variables exists in some state. Example: given state [x -> T] and list [y, z] we get states [x -> T,y -> T, Z -> T], 
  [x -> T,y -> F, z -> T], [x -> T,y -> T, z -> F], [x -> T,y -> F, z -> F] *)
let rec addAllValuations varList states = match varList with
	[] -> states
	| var :: vars -> 
		let nestedStates = rev_map (addTrueAndFalse var) states in
		let newStates = concat nestedStates in
			(addAllValuations vars newStates)
	;;

(* create a list of 2^n different states of n variables, one for each valuation given a list of variables. *)
let allValuations = function 
	[] -> []
	| ( var :: vars ) -> addAllValuations vars (addTrueAndFalse var [])
	;;

(* remove all variables declared in the state from the list *)
let removeDeclared state varList = filter (fun var -> not (containsVar state var)) varList;;

(* remove the variables declared in state that also exist in the list *)
let removeVars state varList = filter (fun (var1, _) -> not (mem var1 varList)) state;;

(* as removeVars but takes a singel variable to be removed *)
let removeVar state var = filter (fun (var1, _) -> not (var = var1)) state;;

(* adds all valuations of the variables in varList. *)
let allValuationsFromState state varList =
	let newState = removeVars state varList in
		addAllValuations varList [ newState ]
	;;

(* returns a list of the variables in state that is true in this state. *)
let trueVarList state = List.map (fun (var, _) -> var) (List.filter trueFilter state);;
	
	